
/*
 * jjyun_fdfs.c
 * 连接fdfs服务器，并执行命令
 * Created on: 2018年6月7日
 * Author: cfm
 */
typedef struct ngx_fdfs_upstream_s ngx_fdfs_upstream_t;

typedef void (*ngx_fdfs_upstream_handler_pt)(ngx_fdfs_upstream_t *u);

ngx_int_t ngx_fdfs_upstream_init(ngx_fdfs_upstream_t *u);

static void ngx_fdfs_handle(ngx_event_t *ev);

static ngx_int_t ngx_fdfs_event_get_peer(ngx_peer_connection_t *pc, void *data);

static void ngx_fdfs_upstream_write_empty_handler(ngx_fdfs_upstream_t *u);
static void ngx_fdfs_upstream_read_empty_handler(ngx_fdfs_upstream_t *u);
static void ngx_fdfs_upstream_write_handler(ngx_fdfs_upstream_t *u);
static void ngx_fdfs_upstream_read_header_handler(ngx_fdfs_upstream_t *u);
static void ngx_fdfs_upstream_read_body_handler(ngx_fdfs_upstream_t *u);

static void ngx_fdfs_free(ngx_fdfs_upstream_t *u);

static ngx_int_t ngx_fdfs_get_delete_storage(ngx_fdfs_upstream_t *u);
static ngx_int_t ngx_fdfs_get_upload_storage(ngx_fdfs_upstream_t *u);
static ngx_int_t ngx_fdfs_get_storage_done(ngx_fdfs_upstream_t *u);

static ngx_int_t ngx_fdfs_delete_file(ngx_fdfs_upstream_t *u);
static ngx_int_t ngx_fdfs_upload_file(ngx_fdfs_upstream_t *u);
static ngx_int_t ngx_fdfs_file_done(ngx_fdfs_upstream_t *u);

#define NGX_FDFS_ERROR_CONNECT -1
#define NGX_FDFS_ERROR_WRITE -2
#define NGX_FDFS_ERROR_READ -3
#define NGX_FDFS_ERROR_PARAM -4
#define NGX_FDFS_ERROR_NOSERVER -5
#define NGX_FDFS_ERROR_UPLOADFAIL -5

//表示是打标签来的连接直接放过
static char g_chfdfssendtime[FDFS_CLIENT_ECVRYPT_LEN] = {0};

typedef void (*ngx_fdfs_callback)(ngx_fdfs_upstream_t *u);

typedef struct ngx_fdfs_ctx_s
{
    ngx_str_t ip;
    in_port_t port;
    ngx_int_t index;

    ngx_str_t fileid;
    ngx_str_t localname;

    ngx_int_t type;

    ngx_fdfs_callback pcall;
} ngx_fdfs_ctx_t;

struct ngx_fdfs_upstream_s
{
    ngx_http_request_t *r;
    ngx_fdfs_ctx_t ctx;
    void *data;

    ngx_peer_connection_t peer;

    ngx_msec_t connect_timeout;
    ngx_msec_t send_timeout;
    ngx_msec_t read_timeout;

    ngx_fdfs_upstream_handler_pt read_event_handler;
    ngx_fdfs_upstream_handler_pt write_event_handler;

    ngx_output_chain_ctx_t output;
    ngx_chain_writer_ctx_t writer;

    ngx_chain_t send;
    ngx_chain_t sendfile;
    ngx_buf_t *header;
    ngx_buf_t *content;

    unsigned request_sent;
    unsigned header_received;
    unsigned content_received;

    unsigned nerr;
    ngx_str_t *err;

    ngx_int_t (*ngx_fdfs_done)(ngx_fdfs_upstream_t *u);
};

ngx_int_t ngx_fdfs_upstream_init(ngx_fdfs_upstream_t *u)
{
    ngx_int_t rc;
    ngx_http_request_t *r = u->r;
    ngx_fdfs_ctx_t *uctx = &u->ctx;
    ngx_peer_connection_t *ppeer = &u->peer;
    ngx_http_core_loc_conf_t *clcf;
    ngx_connection_t *c;

    u->read_event_handler = ngx_fdfs_upstream_read_empty_handler;
    u->write_event_handler = ngx_fdfs_upstream_write_handler;

    ppeer->start_time = ngx_current_msec;
    ppeer->get = ngx_fdfs_event_get_peer;
    ppeer->log = r->connection->log;

    struct sockaddr_in *paddr = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in));

    paddr->sin_family = AF_INET;
    paddr->sin_port = htons(uctx->port);
    paddr->sin_addr.s_addr = inet_addr((char *)uctx->ip.data);

    ppeer->sockaddr = (struct sockaddr *)paddr;
    ppeer->socklen = sizeof(struct sockaddr_in);

    rc = ngx_event_connect_peer(ppeer);
    if (rc == NGX_ERROR || rc == NGX_DECLINED)
    {
        ngx_log_error(NGX_LOG_ERR, ppeer->log, 0,
                      "ngx_event_connect_peer fail ip %V port %d", &uctx->ip, uctx->port);
        u->nerr = NGX_FDFS_ERROR_CONNECT;
        return NGX_ERROR;
    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    c = u->peer.connection;
    c->data = u;

    c->write->handler = ngx_fdfs_handle;
    c->read->handler = ngx_fdfs_handle;

    c->log = r->connection->log;
    c->read->log = c->log;
    c->write->log = c->log;
    c->sendfile &= r->connection->sendfile;

    u->output.alignment = clcf->directio_alignment;
    u->output.pool = r->pool;
    u->output.bufs.num = 1;
    u->output.bufs.size = clcf->client_body_buffer_size;

    if (u->output.output_filter == NULL)
    {
        u->output.output_filter = ngx_chain_writer;
        u->output.filter_ctx = &u->writer;
    }

    u->writer.pool = r->pool;
    u->writer.out = NULL;
    u->writer.last = &u->writer.out;
    u->writer.connection = c;
    u->writer.limit = 0;

    u->request_sent = 0;
    u->header_received = 0;
    u->content_received = 0;
    u->nerr = 0;

    if (rc == NGX_AGAIN)
    {
        ngx_add_timer(c->write, u->connect_timeout);
        return NGX_OK;
    }

    ngx_fdfs_upstream_write_handler(u);
    return NGX_OK;
}

static void ngx_fdfs_handle(ngx_event_t *ev)
{
    ngx_connection_t *c;
    ngx_fdfs_upstream_t *u;

    c = ev->data;
    u = c->data;

    if (ev->delayed && ev->timedout)
    {
        ev->delayed = 0;
        ev->timedout = 0;
    }

    if (ev->write)
    {
        u->write_event_handler(u);
    }
    else
    {
        u->read_event_handler(u);
    }
}

static ngx_int_t ngx_fdfs_event_get_peer(ngx_peer_connection_t *pc, void *data)
{
    return NGX_OK;
}

static void ngx_fdfs_upstream_write_empty_handler(ngx_fdfs_upstream_t *u)
{
}

static void ngx_fdfs_upstream_read_empty_handler(ngx_fdfs_upstream_t *u)
{
}

static void ngx_fdfs_upstream_write_handler(ngx_fdfs_upstream_t *u)
{
    ngx_int_t rc;
    ngx_connection_t *c;
    ngx_chain_t *out;

    c = u->peer.connection;
    if (c->write->timedout)
    {
        ngx_log_error(NGX_LOG_ERR, c->log, 0,
                      "ngx_fdfs_upstream_write_handler timedout");
        u->nerr = NGX_FDFS_ERROR_WRITE;
        ngx_fdfs_free(u);
        return;
    }

    if (!u->request_sent)
    {
        u->request_sent = 1;
        out = &u->send;
    }
    else
    {
        out = NULL;
    }

    rc = ngx_output_chain(&u->output, out);

    if (rc == NGX_AGAIN)
    {
        if (!c->write->ready)
        {
            ngx_add_timer(c->write, u->send_timeout);
        }
        else if (c->write->timer_set)
        {
            ngx_del_timer(c->write);
        }

        if (ngx_handle_write_event(c->write, 0) != NGX_OK)
        {
            ngx_log_error(NGX_LOG_ERR, c->log, 0,
                          "ngx_fdfs_upstream_write_handler ngx_handle_write_event err");
            u->nerr = NGX_FDFS_ERROR_WRITE;
            ngx_fdfs_free(u);
            return;
        }
        return;
    }

    if (rc != NGX_OK)
    {
        ngx_log_error(NGX_LOG_ERR, c->log, 0,
                      "ngx_fdfs_upstream_write_handler ngx_output_chain err");
        u->nerr = NGX_FDFS_ERROR_WRITE;
        ngx_fdfs_free(u);
        return;
    }

    u->write_event_handler = ngx_fdfs_upstream_write_empty_handler;
    u->read_event_handler = ngx_fdfs_upstream_read_header_handler;

    if (c->write->timer_set == 1)
    {
        ngx_del_timer(c->write);
    }
    if (c->read->timer_set == 0)
    {
        ngx_add_timer(c->read, u->read_timeout);
        return;
    }
}

static void ngx_fdfs_upstream_read_header_handler(ngx_fdfs_upstream_t *u)
{
    ssize_t n;
    ngx_connection_t *c;
    ngx_buf_t *buf;
    ngx_int_t sizecontent;
    TrackerHeader *pHeader;

    c = u->peer.connection;
    if (c->read->timedout)
    {
        ngx_log_error(NGX_LOG_ERR, c->log, 0,
                      "ngx_fdfs_upstream_read_header_handler timedout");
        u->nerr = NGX_FDFS_ERROR_READ;
        ngx_fdfs_free(u);
        return;
    }
    if (u->header == NULL)
    {
        u->header = ngx_create_temp_buf(u->r->pool, sizeof(TrackerHeader));
        if (u->header == NULL)
        {
            ngx_log_error(NGX_LOG_ERR, c->log, 0,
                          "ngx_fdfs_upstream_read_header_handler ngx_create_temp_buf err");
            u->nerr = NGX_HTTP_INTERNAL_SERVER_ERROR;
            ngx_fdfs_free(u);
            return;
        }
    }

    buf = u->header;
    if (u->header_received == 0)
    {
        buf->last = buf->start;
        u->header_received = 1;
    }

    for (;;)
    {
        n = c->recv(c, buf->last, buf->end - buf->last);
        if (n == NGX_AGAIN)
        {
            if (ngx_handle_read_event(c->read, 0) != NGX_OK)
            {
                ngx_log_error(NGX_LOG_ERR, c->log, 0,
                              "ngx_fdfs_upstream_read_header_handler ngx_handle_read_event err");
                u->nerr = NGX_FDFS_ERROR_READ;
                ngx_fdfs_free(u);
                return;
            }
            return;
        }
        if (n == NGX_ERROR || n == 0)
        {
            ngx_log_error(NGX_LOG_ERR, c->log, 0,
                          "ngx_fdfs_upstream_read_header_handler recv err");
            u->nerr = NGX_FDFS_ERROR_READ;
            ngx_fdfs_free(u);
            return;
        }

        buf->last += n;

        if (buf->last == buf->end)
        {
            u->header_received = 2;
            pHeader = (TrackerHeader *)buf->start;
            if (pHeader->status == 0)
            {
                sizecontent = buff2long(pHeader->pkg_len);
                if (sizecontent > 0)
                {
                    u->content = ngx_create_temp_buf(u->r->pool, sizecontent);
                    if (u->content != NULL)
                    {
                        u->read_event_handler = ngx_fdfs_upstream_read_body_handler;
                        ngx_fdfs_upstream_read_body_handler(u);
                        return;
                    }
                }
                else
                {
                    u->content_received = 2;
                    u->content = NULL;
                }
            }
            else
            {
                u->nerr = pHeader->status;
            }
        }
        else
        {
            continue;
        }
        break;
    }

    ngx_fdfs_free(u);
    return;
}

static void ngx_fdfs_upstream_read_body_handler(ngx_fdfs_upstream_t *u)
{
    ssize_t n;
    ngx_buf_t *buf;
    ngx_connection_t *c;

    c = u->peer.connection;
    if (c->read->timedout)
    {
        ngx_log_error(NGX_LOG_ERR, c->log, 0,
                      "ngx_fdfs_upstream_read_header_handler recv err");
        u->nerr = NGX_FDFS_ERROR_READ;
        ngx_fdfs_free(u);
        return;
    }
    buf = u->content;
    for (;;)
    {
        n = c->recv(c, buf->last, buf->end - buf->last);
        if (n == NGX_AGAIN)
        {
            if (ngx_handle_read_event(c->read, 0) != NGX_OK)
            {
                ngx_log_error(NGX_LOG_ERR, c->log, 0,
                              "ngx_fdfs_upstream_read_header_handler recv err");
                u->nerr = NGX_FDFS_ERROR_READ;
                ngx_fdfs_free(u);
                return;
            }
            return;
        }
        if (n == NGX_ERROR || n == 0)
        {
            ngx_log_error(NGX_LOG_ERR, c->log, 0,
                          "ngx_fdfs_upstream_read_header_handler recv err");
            u->nerr = NGX_FDFS_ERROR_READ;
            ngx_fdfs_free(u);
            return;
        }

        buf->last += n;

        if (buf->last == buf->end)
        {
            u->content_received = 2;
        }
        else
        {
            continue;
        }
        break;
    }
    ngx_fdfs_free(u);
}

static void ngx_fdfs_free(ngx_fdfs_upstream_t *u)
{
    ngx_close_connection(u->peer.connection);
    u->ngx_fdfs_done(u);
}

static ngx_int_t ngx_fdfs_get_delete_storage(ngx_fdfs_upstream_t *u)
{
    ngx_buf_t *buf;
    ngx_int_t filename_len;
    TrackerHeader *pHeader;
    ngx_http_request_t *r = u->r;
    ngx_fdfs_ctx_t *fdfsctx = &u->ctx;

    char new_file_id[FDFS_GROUP_NAME_MAX_LEN + 128];
    char *group_name;
    char *filename;
    char *pSeperator;

    ngx_snprintf((u_char *)new_file_id, sizeof(new_file_id), "%V%Z", &fdfsctx->fileid);
    pSeperator = strchr(new_file_id, FDFS_FILE_ID_SEPERATOR);
    if (pSeperator == NULL)
    {
        u->nerr = NGX_FDFS_ERROR_PARAM;
        return NGX_ERROR;
    }

    *pSeperator = '\0';
    group_name = new_file_id;
    filename = pSeperator + 1;

    u->ngx_fdfs_done = ngx_fdfs_get_storage_done;

    if (g_tracker_group.server_count <= 0)
    {
        u->nerr = NGX_FDFS_ERROR_NOSERVER;
        return NGX_ERROR;
    }
    ngx_str_set(&fdfsctx->ip, g_tracker_group.servers[0].ip_addr);
    fdfsctx->port = g_tracker_group.servers[0].port;

    buf = ngx_create_temp_buf(r->pool, sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + 128);
    if (buf == NULL)
    {
        u->nerr = NGX_HTTP_INTERNAL_SERVER_ERROR;
        return NGX_ERROR;
    }
    ngx_memzero(buf->start, buf->end - buf->start);

    ngx_snprintf(buf->start + sizeof(TrackerHeader), FDFS_GROUP_NAME_MAX_LEN, "%s%Z", group_name);
    filename_len = snprintf((char *)buf->start + sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN, 128, "%s", filename);

    pHeader = (TrackerHeader *)buf->start;
    long2buff(FDFS_GROUP_NAME_MAX_LEN + filename_len, pHeader->pkg_len);
    pHeader->cmd = TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE;
    memcpy(pHeader->encrpt_client, g_chfdfssendtime, FDFS_CLIENT_ECVRYPT_LEN);

    buf->last = buf->last + sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + filename_len;

    u->send.buf = buf;
    u->send.next = NULL;

    return NGX_OK;
}

static ngx_int_t ngx_fdfs_get_upload_storage(ngx_fdfs_upstream_t *u)
{
    ngx_buf_t *buf;
    TrackerHeader *pHeader;
    ngx_http_request_t *r = u->r;
    ngx_fdfs_ctx_t *fdfsctx = &u->ctx;

    u->ngx_fdfs_done = ngx_fdfs_get_storage_done;

    if (g_tracker_group.server_count <= 0)
    {
        u->nerr = NGX_FDFS_ERROR_NOSERVER;
        return NGX_ERROR;
    }
    ngx_str_set(&fdfsctx->ip, g_tracker_group.servers[0].ip_addr);
    fdfsctx->port = g_tracker_group.servers[0].port;

    buf = ngx_create_temp_buf(r->pool, sizeof(TrackerHeader));
    if (buf == NULL)
    {
        u->nerr = NGX_HTTP_INTERNAL_SERVER_ERROR;
        return NGX_ERROR;
    }
    ngx_memzero(buf->start, buf->end - buf->start);

    pHeader = (TrackerHeader *)buf->start;
    pHeader->cmd = TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE;
    memcpy(pHeader->encrpt_client, g_chfdfssendtime, FDFS_CLIENT_ECVRYPT_LEN);

    buf->last = buf->last + sizeof(TrackerHeader);

    u->send.buf = buf;
    u->send.next = NULL;

    return NGX_OK;
}

static ngx_int_t ngx_fdfs_get_storage_done(ngx_fdfs_upstream_t *u)
{
    ngx_int_t rc;
    char *pbuf;
    ngx_fdfs_ctx_t *fdfsctx = &u->ctx;
    if (u->header_received == 2 && u->content_received == 2)
    {
        pbuf = (char *)u->content->start;
        char *ip = pbuf + FDFS_GROUP_NAME_MAX_LEN;
        int iport = (int)buff2long(pbuf + FDFS_GROUP_NAME_MAX_LEN + IP_ADDRESS_SIZE - 1);

        fdfsctx->ip.data = (u_char *)ip;
        fdfsctx->ip.len = strlen(ip);
        fdfsctx->port = iport;

        if (fdfsctx->type == 1)
        {
            if ((rc = ngx_fdfs_delete_file(u)) != NGX_OK)
            {
                fdfsctx->pcall(u);
                return rc;
            }
        }
        else if (fdfsctx->type == 2)
        {
            fdfsctx->index = *(pbuf + FDFS_GROUP_NAME_MAX_LEN + IP_ADDRESS_SIZE - 1 + FDFS_PROTO_PKG_LEN_SIZE);
            if ((rc = ngx_fdfs_upload_file(u)) != NGX_OK)
            {
                fdfsctx->pcall(u);
                return rc;
            }
        }

        if ((rc = ngx_fdfs_upstream_init(u)) != NGX_OK)
        {
            fdfsctx->pcall(u);
            return rc;
        }
    }
    else
    {
        fdfsctx->pcall(u);
    }
    return NGX_OK;
}

static ngx_int_t ngx_fdfs_delete_file(ngx_fdfs_upstream_t *u)
{
    ngx_buf_t *buf;
    ngx_int_t filename_len;
    TrackerHeader *pHeader;
    ngx_http_request_t *r = u->r;
    ngx_fdfs_ctx_t *fdfsctx = &u->ctx;

    char new_file_id[FDFS_GROUP_NAME_MAX_LEN + 128];
    char *group_name;
    char *filename;
    char *pSeperator;

    ngx_snprintf((u_char *)new_file_id, sizeof(new_file_id), "%V%Z", &fdfsctx->fileid);
    pSeperator = strchr(new_file_id, FDFS_FILE_ID_SEPERATOR);
    if (pSeperator == NULL)
    {
        u->nerr = NGX_FDFS_ERROR_PARAM;
        return NGX_ERROR;
    }

    *pSeperator = '\0';
    group_name = new_file_id;
    filename = pSeperator + 1;

    u->ngx_fdfs_done = ngx_fdfs_file_done;

    buf = ngx_create_temp_buf(r->pool, sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + 128);
    if (buf == NULL)
    {
        u->nerr = NGX_HTTP_INTERNAL_SERVER_ERROR;
        return NGX_ERROR;
    }
    ngx_memzero(buf->start, buf->end - buf->start);

    ngx_snprintf(buf->start + sizeof(TrackerHeader), FDFS_GROUP_NAME_MAX_LEN, "%s%Z", group_name);
    filename_len = snprintf((char *)buf->start + sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN, 128, "%s", filename);

    pHeader = (TrackerHeader *)buf->start;
    long2buff(FDFS_GROUP_NAME_MAX_LEN + filename_len, pHeader->pkg_len);
    pHeader->cmd = STORAGE_PROTO_CMD_DELETE_FILE;
    memcpy(pHeader->encrpt_client, g_chfdfssendtime, FDFS_CLIENT_ECVRYPT_LEN);

    buf->last = buf->last + sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + filename_len;

    u->send.buf = buf;
    u->send.next = NULL;

    return NGX_OK;
}

static ngx_int_t ngx_fdfs_upload_file(ngx_fdfs_upstream_t *u)
{
    ngx_buf_t *buf;
    ngx_buf_t *buffile;
    TrackerHeader *pHeader;
    ngx_http_request_t *r = u->r;
    ngx_fdfs_ctx_t *fdfsctx = &u->ctx;
    char *p;
    ngx_str_t ext;

    u->ngx_fdfs_done = ngx_fdfs_file_done;

    buf = ngx_create_temp_buf(r->pool, sizeof(TrackerHeader) + FDFS_GROUP_NAME_MAX_LEN + 128);
    if (buf == NULL)
    {
        u->nerr = NGX_HTTP_INTERNAL_SERVER_ERROR;
        return NGX_ERROR;
    }
    ngx_memzero(buf->start, buf->end - buf->start);

    buffile = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    if (buffile == NULL)
    {
        u->nerr = NGX_HTTP_INTERNAL_SERVER_ERROR;
        return NGX_ERROR;
    }

    ext.data = (u_char *)strrchr((char *)fdfsctx->localname.data, '.') + 1;
    ext.len = fdfsctx->localname.data + fdfsctx->localname.len - ext.data;

    buffile->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
    if (buffile->file == NULL)
    {
        u->nerr = NGX_HTTP_INTERNAL_SERVER_ERROR;
        return NGX_ERROR;
    }
    buffile->in_file = 1;
    buffile->file->fd = ngx_open_file(fdfsctx->localname.data, NGX_FILE_RDONLY | NGX_FILE_NONBLOCK, NGX_FILE_OPEN, 0);
    buffile->file->log = r->connection->log;
    buffile->file->name = fdfsctx->localname;
    if (buffile->file->fd <= 0)
    {
        u->nerr = NGX_HTTP_INTERNAL_SERVER_ERROR;
        return NGX_ERROR;
    }
    if (ngx_file_info(fdfsctx->localname.data, &buffile->file->info) == NGX_FILE_ERROR)
    {
        u->nerr = NGX_HTTP_INTERNAL_SERVER_ERROR;
        return NGX_ERROR;
    }
    buffile->file_pos = 0;
    buffile->file_last = buffile->file->info.st_size;
    buffile->last_in_chain = 1;
    buffile->last_buf = 1;

    ngx_pool_cleanup_t *cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
    if (cln == NULL)
    {
        u->nerr = NGX_HTTP_INTERNAL_SERVER_ERROR;
        return NGX_ERROR;
    }

    cln->handler = ngx_pool_cleanup_file;
    ngx_pool_cleanup_file_t *clnf = cln->data;
    clnf->fd = buffile->file->fd;
    clnf->name = buffile->file->name.data;
    clnf->log = r->pool->log;

    pHeader = (TrackerHeader *)buf->start;
    p = (char *)buf->start + sizeof(TrackerHeader);

    *p++ = fdfsctx->index;

    long2buff(buffile->file->info.st_size, p);
    p += FDFS_PROTO_PKG_LEN_SIZE;

    memset(p, 0, FDFS_FILE_EXT_NAME_MAX_LEN);
    memcpy(p, ext.data, ext.len);
    p += FDFS_FILE_EXT_NAME_MAX_LEN;

    long2buff(buffile->file->info.st_size + p - (char *)buf->start - sizeof(TrackerHeader), pHeader->pkg_len);

    pHeader->cmd = STORAGE_PROTO_CMD_UPLOAD_FILE;
    memcpy(pHeader->encrpt_client, g_chfdfssendtime, FDFS_CLIENT_ECVRYPT_LEN);

    buf->last = buf->last + ((u_char *)p - buf->start);

    u->sendfile.buf = buffile;
    u->sendfile.next = NULL;
    u->send.buf = buf;
    u->send.next = &u->sendfile;

    return NGX_OK;
}

static ngx_int_t ngx_fdfs_file_done(ngx_fdfs_upstream_t *u)
{
    ngx_fdfs_ctx_t *fdfsctx = &u->ctx;

    if (u->header_received == 2 && u->content_received == 2)
    {
        u->nerr = 0;
        if( fdfsctx->type == 2)
        {
            if( (u->content->end - u->content->start) < FDFS_GROUP_NAME_MAX_LEN )
            {
               u->nerr = NGX_FDFS_ERROR_UPLOADFAIL; 
            }
        }
    }

    fdfsctx->pcall(u);

    return NGX_OK;
}

static ngx_int_t ngx_jjyun_delete_file(ngx_http_request_t *r, ngx_str_t *fileid, void *data, ngx_fdfs_callback pcall)
{
    ngx_fdfs_upstream_t *u;
    ngx_fdfs_ctx_t *fdfsctx;

    u = ngx_pcalloc(r->pool, sizeof(ngx_fdfs_upstream_t));
    if (u == NULL)
    {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    u->r = r;
    u->connect_timeout = 10000;
    u->send_timeout = 60000;
    u->read_timeout = 60000;

    u->data = data;

    fdfsctx = &u->ctx;
    fdfsctx->fileid = *fileid;
    fdfsctx->type = 1;
    fdfsctx->pcall = pcall;

    if (ngx_fdfs_get_delete_storage(u) != NGX_OK)
    {
        return u->nerr;
    }

    if (ngx_fdfs_upstream_init(u) != NGX_OK)
    {
        return u->nerr;
    }

    return NGX_OK;
}

static ngx_int_t ngx_jjyun_upload_file(ngx_http_request_t *r, ngx_str_t *localname, void *data, ngx_fdfs_callback pcall)
{
    ngx_fdfs_upstream_t *u;
    ngx_fdfs_ctx_t *fdfsctx;

    u = ngx_pcalloc(r->pool, sizeof(ngx_fdfs_upstream_t));
    if (u == NULL)
    {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    u->r = r;
    u->connect_timeout = 10000;
    u->send_timeout = 60000;
    u->read_timeout = 60000;

    u->data = data;

    fdfsctx = &u->ctx;
    fdfsctx->localname = *localname;
    fdfsctx->type = 2;
    fdfsctx->pcall = pcall;

    if (ngx_fdfs_get_upload_storage(u) != NGX_OK)
    {
        return u->nerr;
    }

    if (ngx_fdfs_upstream_init(u) != NGX_OK)
    {
        return u->nerr;
    }

    return NGX_OK;
}